﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MongoDB.Bson;
using MongoDB.Driver;
using MongoDB.Driver.Builders;

namespace MongoCamp
{
    /// <summary>
    /// EntityCollection generic query and operate provider class
    /// </summary>
    /// <typeparam name="TPrimaryKeyType"></typeparam>
    /// <typeparam name="TEntityClass"></typeparam>
    public abstract class EntityQueryProvider<TPrimaryKeyType, TEntityClass> 
        where TEntityClass : IEntityCollection, new()
    {
        /// <summary>
        /// get record by primary key
        /// </summary>
        /// <param name="primaryKeyId">primary key value</param>
        /// <returns></returns>
        public static TEntityClass FindOne(TPrimaryKeyType primaryKeyId)
        {
            var entity = GetEntityInstance();
            return MongoHelper.FindOne<TEntityClass>(entity.GetMongoServer(), entity.GetCollectionName(), BsonValue.Create(primaryKeyId));
        }

        /// <summary>
        /// get BsonDocument by primary key
        /// </summary>
        /// <param name="primaryKeyId">primary key value</param>
        /// <returns></returns>
        public static BsonDocument FindOneAsBson(TPrimaryKeyType primaryKeyId)
        {
            var entity = GetEntityInstance();
            return MongoHelper.FindOne<BsonDocument>(entity.GetMongoServer(), entity.GetCollectionName(), BsonValue.Create(primaryKeyId));
        }

        /// <summary>
        /// get single record
        /// </summary>
        /// <param name="query"></param>
        /// <param name="sortBy"></param>
        /// <returns></returns>
        public static TEntityClass FindOne(IMongoQuery query, IMongoSortBy sortBy = null)
        {
            var entity = GetEntityInstance();
            return MongoHelper.FindOne<TEntityClass>(entity.GetMongoServer(), entity.GetCollectionName(), query, Fields.Null, sortBy);
        }

        /// <summary>
        /// get single record
        /// </summary>
        /// <param name="query"></param>
        /// <param name="sortBy"></param>
        /// <param name="fieds"></param>
        /// <returns></returns>
        public static BsonDocument FindOneAsBson(IMongoQuery query, IMongoSortBy sortBy = null, IMongoFields fieds = null)
        {
            var entity = GetEntityInstance();
            return MongoHelper.FindOne<BsonDocument>(entity.GetMongoServer(), entity.GetCollectionName(), query, fieds, sortBy);
        }

        /// <summary>
        /// get all record
        /// </summary>
        /// <returns></returns>
        public static IList<TEntityClass> FindAll()
        {
            var entity = GetEntityInstance();
            return MongoHelper.FindAll<TEntityClass>(entity.GetMongoServer(), entity.GetCollectionName());
        }

        /// <summary>
        /// get all record
        /// </summary>
        /// <returns></returns>
        public static IList<BsonDocument> FindAllAsBson()
        {
            var entity = GetEntityInstance();
            return MongoHelper.FindAll<BsonDocument>(entity.GetMongoServer(), entity.GetCollectionName());
        }

        /// <summary>
        /// get record list
        /// </summary>
        /// <param name="query"></param>
        /// <param name="fields"></param>
        /// <param name="sortBy"></param>
        /// <param name="pageSize"></param>
        /// <param name="pageIndex"></param>
        /// <returns></returns>
        public static IList<TEntityClass> FindList(IMongoQuery query, IMongoFields fields = null, IMongoSortBy sortBy = null, int? pageSize = null, int? pageIndex = null) 
        {
            var entity = GetEntityInstance();
            return MongoHelper.FindList<TEntityClass>(entity.GetMongoServer(), entity.GetCollectionName(), query, fields, sortBy, pageSize, pageIndex);
        }

        /// <summary>
        /// get record list
        /// </summary>
        /// <param name="recordCount"></param>
        /// <param name="query"></param>
        /// <param name="fields"></param>
        /// <param name="sortBy"></param>
        /// <param name="pageSize"></param>
        /// <param name="pageIndex"></param>
        /// <returns></returns>
        public static IList<TEntityClass> FindList(out int recordCount, IMongoQuery query, IMongoFields fields = null, IMongoSortBy sortBy = null, int? pageSize = null, int? pageIndex = null)
        {
            var entity = GetEntityInstance();
            return MongoHelper.FindList<TEntityClass>(out recordCount, entity.GetMongoServer(), entity.GetCollectionName(), query, fields, sortBy, pageSize, pageIndex);
        }

        /// <summary>
        /// get record list
        /// </summary>
        /// <param name="query"></param>
        /// <param name="fields"></param>
        /// <param name="sortBy"></param>
        /// <param name="pageSize"></param>
        /// <param name="pageIndex"></param>
        /// <returns></returns>
        public static IList<BsonDocument> FindListAsBson(IMongoQuery query, IMongoFields fields = null, IMongoSortBy sortBy = null, int? pageSize = null, int? pageIndex = null)
        {
            var entity = GetEntityInstance();
            return MongoHelper.FindList<BsonDocument>(entity.GetMongoServer(), entity.GetCollectionName(), query, fields, sortBy, pageSize, pageIndex);
        }

        /// <summary>
        /// get record list
        /// </summary>
        /// <param name="recordCount"></param>
        /// <param name="query"></param>
        /// <param name="fields"></param>
        /// <param name="sortBy"></param>
        /// <param name="pageSize"></param>
        /// <param name="pageIndex"></param>
        /// <returns></returns>
        public static IList<BsonDocument> FindListAsBson(out int recordCount, IMongoQuery query, IMongoFields fields = null, IMongoSortBy sortBy = null, int? pageSize = null, int? pageIndex = null)
        {
            var entity = GetEntityInstance();
            return MongoHelper.FindList<BsonDocument>(out recordCount, entity.GetMongoServer(), entity.GetCollectionName(), query, fields, sortBy, pageSize, pageIndex);
        }

        /// <summary>
        /// get record count
        /// </summary>
        /// <param name="query"></param>
        /// <returns></returns>
        public static long GetCount(IMongoQuery query = null)
        {
            var entity = GetEntityInstance();
            return MongoHelper.GetCount(entity.GetMongoServer(), entity.GetCollectionName(), query);
        }

        /// <summary>
        /// update record
        /// </summary>
        /// <param name="query"></param>
        /// <param name="update"></param>
        /// <param name="flag"></param>
        /// <param name="writeConcern"></param>
        /// <returns></returns>
        public static WriteConcernResult Update(IMongoQuery query, IMongoUpdate update, UpdateFlags flag, WriteConcern writeConcern = null)
        {
            var entity = GetEntityInstance();
            return MongoHelper.Update(entity.GetMongoServer(), entity.GetCollectionName(), query, update, flag, writeConcern);
        }

        /// <summary>
        /// update single record
        /// </summary>
        /// <param name="query"></param>
        /// <param name="update"></param>
        /// <param name="writeConcern"></param>
        /// <returns></returns>
        public static WriteConcernResult UpdateOne(IMongoQuery query, IMongoUpdate update, WriteConcern writeConcern = null)
        {
            return Update(query, update, UpdateFlags.None, writeConcern);
        }


        /// <summary>
        /// Update Or Insert document
        /// </summary>
        /// <param name="query"></param>
        /// <param name="updateDocument"></param>
        /// <param name="flag"></param>
        /// <param name="writeConcern"></param>
        /// <returns></returns>
        public static WriteConcernResult Update(IMongoQuery query, BsonDocument updateDocument, UpdateFlags flag = UpdateFlags.Upsert, WriteConcern writeConcern = null)
        {
            var entity = GetEntityInstance();
            return MongoHelper.Update(entity.GetMongoServer(), entity.GetCollectionName(), query, updateDocument, flag, writeConcern);
        }

        /// <summary>
        /// Update Or Insert document
        /// </summary>
        /// <param name="primaryKeyId"></param>
        /// <param name="updateDocument"></param>
        /// <param name="flag"></param>
        /// <param name="writeConcern"></param>
        /// <returns></returns>
        public static WriteConcernResult Update(TPrimaryKeyType primaryKeyId, BsonDocument updateDocument, UpdateFlags flag = UpdateFlags.Upsert, WriteConcern writeConcern = null)
        {
            return Update(Query.EQ(PrimaryKey.Field, primaryKeyId.ToBsonValue()), updateDocument, flag, writeConcern);
        }


        /// <summary>
        /// update record
        /// </summary>
        /// <param name="query"></param>
        /// <param name="update"></param>
        /// <param name="writeConcern"></param>
        /// <returns></returns>
        public static WriteConcernResult UpdateAll(IMongoQuery query, IMongoUpdate update, WriteConcern writeConcern = null)
        {
            return Update(query, update, UpdateFlags.Multi, writeConcern);
        }

        /// <summary>
        /// remove single record by primary key
        /// </summary>
        /// <param name="primaryKeyId"></param>
        /// <returns></returns>
        public static WriteConcernResult Remove(TPrimaryKeyType primaryKeyId)
        {
            var query = Query.EQ(PrimaryKey.Field, BsonValue.Create(primaryKeyId));
            return Remove(query, RemoveFlags.Single);
        }

        /// <summary>
        /// remove single record by query
        /// </summary>
        /// <param name="query"></param>
        /// <param name="flags"></param>
        /// <param name="writeConcern"></param>
        /// <returns></returns>
        public static WriteConcernResult Remove(IMongoQuery query, RemoveFlags flags = RemoveFlags.None, WriteConcern writeConcern = null)
        {
            var entity = GetEntityInstance();
            return MongoHelper.Remove(entity.GetMongoServer(), entity.GetCollectionName(), query);
        }

        /// <summary>
        /// remove all record
        /// </summary>
        /// <returns></returns>
        public static WriteConcernResult RemoveAll()
        {
            var entity = GetEntityInstance();
            return MongoHelper.RemoveAll(entity.GetMongoServer(), entity.GetCollectionName());
        }

        /// <summary>
        /// save record
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="flags"></param>
        /// <param name="writeConcern"></param>
        /// <returns></returns>
        public static WriteConcernResult Save(TEntityClass entity, InsertFlags flags = InsertFlags.None, WriteConcern writeConcern = null)
        {
            return MongoHelper.Save(entity.GetMongoServer(), entity.GetCollectionName(), entity, flags, writeConcern);
        }

        /// <summary>
        /// save record
        /// </summary>
        /// <param name="document"></param>
        /// <param name="flags"></param>
        /// <param name="writeConcern"></param>
        /// <returns></returns>
        public static WriteConcernResult Save(BsonDocument document, InsertFlags flags = InsertFlags.None, WriteConcern writeConcern = null)
        {
            var entity = GetEntityInstance();
            return MongoHelper.Save(entity.GetMongoServer(), entity.GetCollectionName(), document, flags, writeConcern);
        }

        /// <summary>
        /// insert single record
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="flags"></param>
        /// <param name="writeConcern"></param>
        /// <returns></returns>
        public static WriteConcernResult Insert(TEntityClass entity, InsertFlags flags = InsertFlags.None, WriteConcern writeConcern = null)
        {
            return MongoHelper.Insert(entity.GetMongoServer(), entity.GetCollectionName(), entity, flags, writeConcern);
        }

        /// <summary>
        /// insert single record
        /// </summary>
        /// <param name="document"></param>
        /// <param name="flags"></param>
        /// <param name="writeConcern"></param>
        /// <returns></returns>
        public static WriteConcernResult Insert(BsonDocument document, InsertFlags flags = InsertFlags.None, WriteConcern writeConcern = null)
        {
            var entity = GetEntityInstance();
            return MongoHelper.Insert(entity.GetMongoServer(), entity.GetCollectionName(), document, flags, writeConcern);
        }

        /// <summary>
        /// insert record list
        /// </summary>
        /// <param name="entityList"></param>
        /// <param name="flags"></param>
        /// <param name="writeConcern"></param>
        /// <returns></returns>
        public static IEnumerable<WriteConcernResult> InsertBatch(IList<TEntityClass> entityList, InsertFlags flags = InsertFlags.None, WriteConcern writeConcern = null)
        {
            if (entityList.IsNullOrEmpty())
                return null;
            var entity = entityList[0];
            return MongoHelper.InsertBatch(entity.GetMongoServer(), entity.GetCollectionName(), entityList, flags, writeConcern);
        }

        /// <summary>
        /// insert record list
        /// </summary>
        /// <param name="documentList"></param>
        /// <param name="flags"></param>
        /// <param name="writeConcern"></param>
        /// <returns></returns>
        public static IEnumerable<WriteConcernResult> InsertBatch(IList<BsonDocument> documentList, InsertFlags flags = InsertFlags.None, WriteConcern writeConcern = null)
        {
            if (documentList.IsNullOrEmpty())
                return null;
            var entity = GetEntityInstance();
            return MongoHelper.InsertBatch(entity.GetMongoServer(), entity.GetCollectionName(), documentList, flags, writeConcern);
        }

        /// <summary>
        /// Dorp current collection
        /// </summary>
        /// <returns></returns>
        public static CommandResult DorpCollection()
        {
            var entity = GetEntityInstance();
            return MongoHelper.DorpCollection(entity.GetMongoServer(), entity.GetCollectionName());
        }

        /// <summary>
        /// Get current Instance
        /// </summary>
        /// <returns></returns>
        protected static IEntityCollection GetEntityInstance()
        {
            try
            {
                IEntityCollection entity = Activator.CreateInstance<TEntityClass>();
                return entity;
            }
            catch (Exception exp)
            {
                throw new Exception(string.Format("{0} create instance fail.", typeof(TEntityClass).Name), exp.InnerException);
            }
        }

    }
}
